import { getRandomId } from "@craftjs/utils";
/**
 * 二维数组键值转化成对象
 * @param nodeEntires  二维数组键值
 * @returns Object 键值对
 */
const fromEntries = (nodeEntires) => {
	// @ts-ignore 支持新方法就使用
	if (Object.fromEntries) {
		// @ts-ignore
		return Object.fromEntries(nodeEntires);
	}
	// 不支持则使用数组的reduce兼容
	return nodeEntires.reduce(
		(accum, [id, value]) => ({
			...accum,
			[id]: value,
		}),
		{}
	);
};
/**
 * 克隆NodeTree
 * @param query 编辑器的查询方法
 * @param nodeTree NodeTree数据
 * @returns 新的NodeTree
 */
const getCloneTree = (query, nodeTree) => {
	// 存放新节点键值对的对象
	const newNodesMap = {};
	// 更改节点id，并且把节点转化成新节点（FreshNode）
	const changeNodeId = (node, newParentId) => {
		// 生成新的节点id
		const newNodeId = getRandomId();
		// 循环转化当前节点的所有子节点
		const childNodes = node.data.nodes.map((childId) =>
			changeNodeId(nodeTree.nodes[childId], newNodeId)
		);
		// 循环转化当前节点的所有关联节点
		const linkedNodes = Object.keys(node.data.linkedNodes).reduce(
			(nodesMap, id) => {
				const newLinkedNodeId = changeNodeId(
					nodeTree.nodes[node.data.linkedNodes[id]],
					newNodeId
				);
				return {
					...nodesMap,
					[id]: newLinkedNodeId,
				};
			},
			{}
		);
		// 组合成临时Node结构，已实现子节点id、关联接点id替换
		let tmpNode = {
			...node,
			// 替换当前节点id
			id: newNodeId,
			data: {
				...node.data,
				// 替换父节点id
				parent: newParentId || node.data.parent,
				// 使用新的子节点ids
				nodes: childNodes,
				// 使用新的关联节点ids
				linkedNodes,
			},
		};
		// 把临时的节点转化成新节点（FreshNode）
		let freshNode = query.parseFreshNode(tmpNode).toNode();
		// 存储转化后的节点，id=>Node结构的键值对
		newNodesMap[newNodeId] = freshNode;
		// 返回当前节点的新id，方便递归调用使用
		return newNodeId;
	};
	// @ts-ignore 更改根节点的id，内部会递归循环更改子节点和关联节点
	const rootNodeId = changeNodeId(nodeTree.nodes[nodeTree.rootNodeId]);
	// 返回新的NodeTree
	return {
		rootNodeId,
		nodes: newNodesMap,
	};
};
/**
 * 保存片段
 * @param nodeId 需要存储片段的根节点id
 * @param query 编辑器的查询方法
 * @returns 存储片段JSON
 */
export const saveFragment = (nodeId, query) => {
	// 根据id查询node并转化成NodeTree
	const tree = query.node(nodeId).toNodeTree();
	// 把NodeTree中的nodes循环转化成每项是[id, SerializedNode]的数组
	// 		这样结构方便使用Object.fromEntries方法转化成键值对对象
	// 		此方法在ES2019版本中出现，本项目ES2017，因此写了一个兼容方法
	const nodeEntires = Object.keys(tree.nodes).map((id) => [
		id,
		query.node(id).toSerializedNode(),
	]);
	// 使用fromEntries方法把二维数组转化成对象，再转化成字符串方便存储
	const serializedNodesJSON = JSON.stringify(fromEntries(nodeEntires));
	// 组合成类似NodeTree的数据结构
	const saveData = {
		rootNodeID: tree.rootNodeId,
		nodes: serializedNodesJSON,
	};
	return saveData;
};
/**
 * 转化存储数据
 * @param query 编辑器的查询方法
 * @param data 存储片段数据
 * @returns NodeTree数据
 */
export const pasteSerializedNodesTree = (query, data) => {
	// 把nodes字符串转化成对象
	const newNodes = JSON.parse(data.nodes);
	// 把nodes循环转化成每项是[id, Node]的数组
	const nodeEntires = Object.keys(newNodes).map((id) => {
		// 缓存nodeId，方便后面使用
		let nodeId = id;
		// 把SerializedNode转化成Node数据，转化过程中还原原来nodeId，避免原来父子关系中断
		return [
			nodeId,
			query
				.parseSerializedNode(newNodes[id])
				.toNode((node) => (node.id = nodeId)),
		];
	});
	// 复用fromEntries方法把二维数组转化成对象，并组成NodeTree结构
	const nodeTree = {
		rootNodeId: data.rootNodeID,
		nodes: fromEntries(nodeEntires),
	};
	// 克隆NodeTree中的Node生成新的Node，返回全新的NodeTree
	//			克隆代码较多，单独封装方法getCloneTree
	return getCloneTree(query, nodeTree);
};
